package com.stellar.web.core.handler;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.exc.InvalidFormatException;
import com.fasterxml.jackson.databind.exc.MismatchedInputException;
import com.stellar.exception.ServiceException;
import com.stellar.web.core.bean.Result;
import com.stellar.web.core.exception.CryptoException;
import com.stellar.web.core.exception.EmailSendException;
import com.stellar.web.core.exception.PermissionException;
import com.stellar.web.core.exception.SmsSendException;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.validation.BindException;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import static jdk.nashorn.internal.runtime.regexp.joni.Config.log;

/**
 * @Author Jqcc
 * @Version 1.0
 * @Data 2025/2/14
 * <p>
 * 全局异常处理器
 */
@RestControllerAdvice
@AllArgsConstructor
@Slf4j
public class GlobalExceptionHandler {

    /**
     * 处理业务异常 ServiceException
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(value = ServiceException.class)
    public Result<?> serviceExceptionHandler(ServiceException ex) {
        return Result.fail(ex.getCode(), ex.getMessage());
    }


    /**
     * 处理表单绑定异常（@Validated 触发的校验异常）
     */
    @ExceptionHandler(BindException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Map<String, String>> handleBindException(BindException ex) {
        Map<String, String> errors = new HashMap<>();
        List<FieldError> fieldErrors = ex.getFieldErrors();
        for (FieldError fieldError : fieldErrors) {
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        }
        return Result.fail(HttpStatus.BAD_REQUEST.value(), "参数校验失败", errors);
    }

    /**
     * 处理请求体绑定异常（@Validated 触发的校验异常）
     */
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Map<String, String>> handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) {
        Map<String, String> errors = new HashMap<>();
        ex.getBindingResult().getFieldErrors().forEach(fieldError -> {
            errors.put(fieldError.getField(), fieldError.getDefaultMessage());
        });
        return Result.fail(HttpStatus.BAD_REQUEST.value(), "参数校验失败", errors);
    }

    /**
     * 处理参数校验异常（@RequestParam、@PathVariable 触发的校验异常）
     */
    @ExceptionHandler(ConstraintViolationException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<Map<String, String>> handleConstraintViolationException(ConstraintViolationException ex) {
        Map<String, String> errors = new HashMap<>();
        Set<ConstraintViolation<?>> violations = ex.getConstraintViolations();
        for (ConstraintViolation<?> violation : violations) {
            String field = violation.getPropertyPath().toString();
            String message = violation.getMessage();
            errors.put(field, message);
        }
        return Result.fail(HttpStatus.BAD_REQUEST.value(), "参数校验失败", errors);
    }

    /**
     * 处理请求体解析异常（类型错误/格式错误/缺失）
     */
    @ExceptionHandler(HttpMessageNotReadableException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Result<String> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
        log.error("请求体解析异常: {}", ex.getMessage());  // 记录原始错误信息
        String errorMessage = analyzeException(ex);      // 解析异常原因
        return Result.fail(HttpStatus.BAD_REQUEST.value(), errorMessage);
    }

    /**
     * 解析异常原因并生成友好提示
     */
    private String analyzeException(HttpMessageNotReadableException ex) {
        //  处理请求体缺失的情况
        if (ex.getMessage() != null && ex.getMessage().contains("Required request body is missing")) {
            return "请求体缺失，请检查POST/PUT请求是否携带参数";
        }

        // 获取根本原因并分类处理
        Throwable rootCause = ex.getRootCause();
        if (rootCause instanceof JsonParseException) {
            return "请求体JSON格式错误，请检查JSON语法";
        } else if (rootCause instanceof InvalidFormatException) {
            return buildInvalidFormatMessage((InvalidFormatException) rootCause);
        } else if (rootCause instanceof MismatchedInputException) {
            return buildMismatchMessage((MismatchedInputException) rootCause);
        }

        //  默认提示
        return "请求参数错误，请检查参数格式和类型";
    }

    /**
     * 处理参数类型不匹配（如字符串转数字）
     */
    private String buildInvalidFormatMessage(InvalidFormatException ex) {
        StringBuilder message = new StringBuilder("参数类型错误：");
        if (ex.getPath() != null && !ex.getPath().isEmpty()) {
            message.append("字段 [")
                    .append(buildFieldPath(ex.getPath()))
                    .append("] 需要 ")
                    .append(ex.getTargetType().getSimpleName())
                    .append(" 类型");
        }
        return message.toString();
    }

    /**
     * 处理字段类型不匹配（如缺少必需字段）
     */
    private String buildMismatchMessage(MismatchedInputException ex) {
        StringBuilder message = new StringBuilder("参数错误：");
        if (ex.getPath() != null && !ex.getPath().isEmpty()) {
            message.append("字段 [")
                    .append(buildFieldPath(ex.getPath()))
                    .append("] 类型不匹配");
        } else {
            message.append("请求体缺少必需字段或类型不正确");
        }
        return message.toString();
    }

    /**
     * 构建字段层级路径（如 user.address.street）
     */
    private String buildFieldPath(List<JsonMappingException.Reference> path) {
        return path.stream()
                .filter(ref -> ref.getFieldName() != null)
                .map(JsonMappingException.Reference::getFieldName)
                .collect(Collectors.joining("."));
    }

    /**
     * 处理邮件发送异常
     */
    @ExceptionHandler(EmailSendException.class)
    public Result handleEmailError(EmailSendException ex) {
        return Result.fail(HttpStatus.BAD_REQUEST.value(), "邮件发送失败");
    }

    /**
     * 处理手机验证码发送异常
     */
    @ExceptionHandler(SmsSendException.class)
    public Result handleSmsError(SmsSendException ex) {
        return Result.fail(HttpStatus.BAD_REQUEST.value(), ex.getMessage());
    }

    /**
     * 处理加密解密异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(CryptoException.class)
    public Result handleCryptoException(CryptoException ex) {
        return Result.fail(HttpStatus.BAD_REQUEST.value(), "加密解密失败");
    }


    /**
     * 处理权限异常
     *
     * @param ex
     * @return
     */
    @ExceptionHandler(PermissionException.class)
    public Result handlePermissionException(PermissionException ex) {
        return Result.fail(HttpStatus.FORBIDDEN.value(), ex.getMessage());
    }

}
